/*******************************************************************************
 * @note Copyright (C) 2011-2022， Shanghai Panchip Microelectronics Co., Ltd. 
 * @SPDX-License-Identifier: Apache-2.0
 * @history - V0.1, 2022-09-28
*******************************************************************************/
#include "hardware.h"
#include "pan3029.h"
#include "radio.h"
#include "misc.h"
#include "at.h"
#include "pn_atcmd.h"
#include "user.h"
#include "service_pt.h"
#include "at_log.h"
#include "easyflash.h"
#include "chirplan.h"
#include "service_nodemode.h"
#include "service_net.h"
#include "dmac.h"
#include "math.h"

node_config_t node_config = {
    .work_mode = WORKMODE_NET,//WORKMODE_PT,
    .pt_class = PT_CLASS_PT,
    .net_class = CHIRP_CLASS_C,
    .node_id = 0xABCD7711,
    .app_id = 0xAA,
    .gw_id = 0x55,
    .channel = 433010000,
    .channel_up   = {433010000, 432060000, 431120000, 430170000, 429230000, 428280000, 427340000, 426390000},
    .channel_down = {434450000, 435500000, 436560000, 437610000, 438670000, 439720000, 440780000, 441830000},
//    .channel_up   = {485290000, 460080000, 464120000, 468160000, 472210000, 477250000, 482340000, 489380000},
//    .channel_down = {497640000, 493430000, 501480000, 506520000, 509570000, 513690000, 518780000, 523830000},
    .rate = 7,
    .preamtime = 0,
    .wakeup_time = 7000,
    .rf_rx_timeout = 5000,
	.rf_rx_window = 1000,
    .cad_check_en = 0,
    .uart_baudrate = 115200,
    .uart_databit = 8,
    .uart_stopbit = 1,
    .uart_parity = 0,
    .uart_interval = 5,
    .aes_enable = 1,
    .aes_key = {0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x30,0x61,0x62,0x63,0x64,0x65,0x66},
    .addr0 = 100,
    .addr1 = 100,
    .aux_mode = AUX_MODE_EMPTY,
    .aux_head_time = 5,
    .aux_tail_time = 2,
    .net_dack_enbale = TRUE,
    .at_ack_type = 0,
};

rf_config_t pn3029_config = {
    .freq = DEFAULT_FREQ,
    .cr = DEFAULT_CR,
    .bw = DEFAULT_BW,
    .sf = DEFAULT_SF,
    .pwr = 25,
    .crc = CRC_ON,
    .ldr = LDR_OFF,
    .modemMode = MODEM_MODE_NORMAL,
    .preamble = 8,
    .dcdc = DCDC_OFF,
};

const table_rate_t g_table_rate[11]=
{
  /*rate  bps    CR  SF   BW*/
    {0,   146,   1,  12,  6},
    {1,   269,   1,  11,  6},
    {2,   488,   1,  10,  6},
    {3,   537,   1,  11,  7},
    {4,   879,   1,  9,   6},
    {5,   977,   1,  10,  7},
    {6,   1758,  1,  9,   7},
    {7,   3125,  1,  8,   7},
    {8,   6250,  1,  8,   8},
    {9,   1093,  1,  7,   8},
    {10,  21875, 1,  7,   9},
};

static int rf_rx_len;
static u8 *p_rf_rx_buf = app_data.RxBuff;
static u8 *p_rf_tx_buf = app_data.TxBuff;

/* rf irq flag only for at server */
static u8 at_rf_irq_flag;

/* ota_ram_flag is for handshaking between application and bootloader.
 * if user want to upgrade firmware,he/she firstly set ota_ram_flag to 0x55AA55AA,
 * then reboot node, then the node enter bootloader, if bootloader check ota_ram_flag is equal 0x55AA55AA,
 * it will wait pc ota cmd until timeout. if ota_ram_flag is not equal 0x55AA55AA, if possible, bootloader will jump to 
 * application.
 */
static u32 ota_ram_flag __attribute__((at(0x20001FFC))) = 0;

extern u32 uart_packing_interval;
extern u32 uart_idle_timer_cnt;
extern bool g_at_ack_type;

int userBreakCheck(void)
{
    char ch;
    int len;

    do{
        len = uart_fifo_read((u8 *)&ch, 1);
        /* combination key CTRL+A/B/C related to 01/02/03.....ctrl+c = 3 */ 
        if(len > 0)     // input ctrl+c will break rx task
        {
            if(ch == 3  /*ctrl+c*/
               || ch == 'c'
               || ch == 'C')
            {
                uart_rx_fifo_flush();
                return 1;
            }
        }
        else
        {
            break;
        }
    }while(1);

    return 0;
}

void at_rf_irq_process( void )
{
    extern uint8_t pan3029_irq_trigged_flag;
    
    if( pan3029_irq_trigged_flag )
    {
        uint8_t irq, size;
        int16_t rssi,i;
        int8_t snr;

        pan3029_irq_trigged_flag = false;
        irq = PAN3029_get_irq();
        
        at_debug("irq:%x",irq);
        
        if(irq & REG_IRQ_RX_DONE)
        {
            at_debug("rxlen:%d",size);
            snr = PAN3029_get_snr();
            rssi = PAN3029_get_rssi();

            rf_rx_len = PAN3029_recv_packet(p_rf_tx_buf);
            
            PAN3029_clr_irq();
        }
        if(irq & REG_IRQ_CRC_ERR)
        {           
            PAN3029_clr_irq();
        }
        if(irq & REG_IRQ_RX_TIMEOUT)
        {       
            PAN3029_clr_irq();
        }
        if(irq & REG_IRQ_TX_DONE)
        {
            PAN3029_clr_irq();
        }

        at_rf_irq_flag = irq;
    }
}

static at_result_t at_get_package_time(const char *args)
{
    const float bw_table[10] = {0,0,0,0,0,0,62.5,125,250,500};
    rf_config_t *p = &pn3029_config;

    if(p->bw < 6 ||p->bw > 9)
    {
        return AT_RESULT_FAILE;
    }
    
    int payload_len, argc;
    const char *req_expr = "=%d";
    
    argc = at_req_parse_args(args, req_expr, &payload_len);
    if (argc != 1)
    {
        return AT_RESULT_PARSE_FAILE;
    }

    if ( payload_len > 255 )
    {
        return AT_RESULT_PARSE_FAILE;
    }

    int msg_format = 1;                 //message format
    float symbol_len = (float)(1<<p->sf)/bw_table[p->bw]; //symbol length
    float preamble_time;                //preamble time:ms
    float payload_time = 0;             //payload time:ms
    float total_time;                   //total time:ms
    
    if (p->sf < 7)
    {
        preamble_time = (p->preamble+6.25f)*symbol_len;
    }
    else
    {
        preamble_time = (p->preamble+4.25f)*symbol_len;
    }
    
    if (payload_len > 0)
    {
        payload_time = ceil((float)(payload_len*8-p->sf*4+28-(1-msg_format)*20+16)/((p->sf-p->ldr*2)*4));
        payload_time = payload_time*(p->cr+4);
        payload_time = payload_time+8;
        payload_time = payload_time*symbol_len;        
    }

    total_time = preamble_time+payload_time;
    at_server_printfln("+PTIME=%d", (int)total_time);
    
    return AT_RESULT_OK;
}
AT_CMD_EXPORT("AT+PTIME", "=<payloadlen>", PN_NULL, PN_NULL, at_get_package_time, PN_NULL);

static at_result_t at_uart_query(void)
{
    at_server_printfln("+UART=%d,%d,%d,%d", node_config.uart_baudrate,
                                            node_config.uart_databit,
                                            node_config.uart_stopbit,
                                            node_config.uart_parity);
    return AT_RESULT_OK;
}

static at_result_t at_uart_setup(const char *args)
{
    int baudrate, databit, stopbit, parity, argc;
    const char *req_expr = "=%d,%d,%d,%d";
    extern u32 uart_baudrate;

    argc = at_req_parse_args(args, req_expr, &baudrate, &databit, &stopbit, &parity);
    if (argc != 4)
    {
        return AT_RESULT_PARSE_FAILE;
    }
    
    if ( baudrate == 9600\
         || baudrate == 19200\
         || baudrate == 38400\
         || baudrate == 57600\
         || baudrate == 115200)
    {
        uart_baudrate = baudrate;
        node_config.uart_baudrate = baudrate;
    }
    else
    {
        return AT_RESULT_CHECK_FAILE;
    }

    /* reserved, have no effect */
    node_config.uart_databit = databit;
    node_config.uart_stopbit = stopbit;
    node_config.uart_parity = parity;
    
    at_server_printfln("OK");

    delay1ms(1);

    hw_uart_cfg();

    return AT_RESULT_NULL;
}
AT_CMD_EXPORT("AT+UART", "=<baudrate:9600,19200,38400,57600,115200>,<databit:7,8,9>,<stopbit:1,2>,<parity:none-0,odd-1,even-2>", PN_NULL, at_uart_query, at_uart_setup, PN_NULL);

static at_result_t at_entm_exec(void)
{
    at_debug("exit at mode, enter pt mode");

    /* enter data mode and exit at mode */
    nm_enter_data_mode();
    nm_set_at_op();

    return AT_RESULT_OK;
}
AT_CMD_EXPORT("AT+ENTM", PN_NULL, PN_NULL, PN_NULL, PN_NULL, at_entm_exec);

static at_result_t at_cfgtf_exec(void)
{
#ifdef EF_USING_ENV
    /* write user setting to flash */
    node_store_setting();
#endif
    return AT_RESULT_OK;
}
AT_CMD_EXPORT("AT+SAVE", PN_NULL, NULL, PN_NULL, PN_NULL, at_cfgtf_exec);

static at_result_t at_reld_exec(void)
{
#ifdef EF_USING_ENV
    /* delete all user setting */
    node_delete_setting();
#endif

    at_server_printfln("OK");

    /* reboot node, restore factory settings*/
    NVIC_SystemReset();

    return AT_RESULT_NULL;
}
AT_CMD_EXPORT("AT+DEFAULT", PN_NULL, NULL, PN_NULL, PN_NULL, at_reld_exec);

static at_result_t at_get_ver(void)
{
    at_server_printfln("+VER=%d.%d.%d", PN_VERSION, PN_SUBVERSION, PN_REVISION);

    return AT_RESULT_OK;
}
AT_CMD_EXPORT("AT+VER", PN_NULL, PN_NULL, at_get_ver, PN_NULL, PN_NULL);

static at_result_t at_get_node_name(void)
{
    at_server_printfln("+NAME=%s", NONE_NAME);

    return AT_RESULT_OK;
}
AT_CMD_EXPORT("AT+NAME", PN_NULL, PN_NULL, at_get_node_name, PN_NULL, PN_NULL);

static at_result_t at_get_rto(void)
{
    at_server_printfln("+RTO=%d", node_config.rf_rx_timeout);

    return AT_RESULT_OK;
}

static at_result_t at_set_rto(const char *args)
{
    int rf_rx_timeout, argc;
    const char *req_expr = "=%d";

    argc = at_req_parse_args(args, req_expr, &rf_rx_timeout);
    if(argc != 1)
    {
        return AT_RESULT_PARSE_FAILE;
    }

    at_debug("rf_rx_timeout:%u", rf_rx_timeout);
    node_config.rf_rx_timeout = rf_rx_timeout;

    return AT_RESULT_OK;
}
AT_CMD_EXPORT("AT+RTO", "=<rf recv timeout:1000~15000ms>", PN_NULL, at_get_rto, at_set_rto, PN_NULL);

static at_result_t at_get_waketime(void)
{
    at_server_printfln("+WTM=%d", node_config.wakeup_time);
    return AT_RESULT_OK;
}

static at_result_t at_get_gap_time(void)
{
    at_server_printfln("+RXGAS=%d", node_config.wakeup_time);
    return AT_RESULT_OK;
}

static at_result_t at_set_waketime(const char *args)
{
    int waketime, argc;
    const char *req_expr = "=%d";
    
    argc = at_req_parse_args(args, req_expr, &waketime);
    if(argc != 1)
    {
        return AT_RESULT_PARSE_FAILE;
    }
    
    if ( waketime > WAKEUP_TIME_MAX )
    {
        return AT_RESULT_PARSE_FAILE;
    }
    else if ( waketime < WAKEUP_TIME_MIN )
    {
        if ( waketime != 0 )
        {
            return AT_RESULT_PARSE_FAILE;
        }
    }
    
    at_debug("waketime:%u", waketime);
    node_config.wakeup_time = waketime;

    return AT_RESULT_OK;
}
AT_CMD_EXPORT("AT+RXGAS", "=<wakeup time:100~5000ms;if it is zero,it means that wakeup mode is disabled>", NULL, at_get_gap_time, at_set_waketime, PN_NULL);

static at_result_t at_get_uart_interval(void)
{
    at_server_printfln("+UARTINT=%d", node_config.uart_interval);
    return AT_RESULT_OK;
}

static at_result_t at_set_uart_interval(const char *args)
{
    int uart_interval, argc;
    const char *req_expr = "=%d";

    argc = at_req_parse_args(args, req_expr, &uart_interval);
    if(argc != 1)
    {
        return AT_RESULT_PARSE_FAILE;
    }

    at_debug("uart_interval:%u", uart_interval);

    if(uart_interval < UART_INTERVAL_MIN 
       || uart_interval > UART_INTERVAL_MAX)
    {
        return AT_RESULT_CHECK_FAILE;
    }

    node_config.uart_interval = uart_interval;
    uart_packing_interval = uart_interval;
    uart_idle_timer_cnt = SystemCoreClock/uart_interval/16000;
    uart_idle_timer_cnt = 0x10000 - uart_idle_timer_cnt;

    return AT_RESULT_OK;
}
AT_CMD_EXPORT("AT+UARTINT", "=<uart packing interval:1~100ms>", PN_NULL, at_get_uart_interval, at_set_uart_interval, PN_NULL);

static at_result_t at_get_channel(void)
{
    at_server_printfln("+CH=%u", node_config.channel);
    return AT_RESULT_OK;
}

static at_result_t at_set_channel(const char *args)
{
    int argc;
    const char *req_expr = "=%u";
	u32 channel;

    argc = at_req_parse_args(args, req_expr, &channel);
    if(argc != 1)
    {
        return AT_RESULT_PARSE_FAILE;
    }

    at_debug("channel:%u", channel);

    node_config.channel = channel;

    pn3029_config.freq = channel;

    return AT_RESULT_OK;
}
AT_CMD_EXPORT("AT+CH", "=<channel:(Hz)>", PN_NULL, at_get_channel, at_set_channel, PN_NULL);

static at_result_t at_get_channel_up(void)
{
    u32 *up = node_config.channel_up;

    at_server_printfln("+UCH=%u,%u,%u,%u,%u,%u,%u,%u", up[0], up[1], up[2], up[3],
                                                       up[4], up[5], up[6], up[7]);
    return AT_RESULT_OK;
}

static at_result_t at_set_channel_up(const char *args)
{
    int argc;
    const char *req_expr = "=%u,%u,%u,%u,%u,%u,%u,%u";
    u32 up[8];

    argc = at_req_parse_args(args, req_expr, &up[0], &up[1], &up[2], &up[3],
                                             &up[4], &up[5], &up[6], &up[7]);
    if(argc != 8)
    {
        return AT_RESULT_PARSE_FAILE;
    }

    memcpy(node_config.channel_up, up, sizeof(up));

    return AT_RESULT_OK;
}
AT_CMD_EXPORT("AT+UCH", "=<f0>,<f1>,<f2>,<f3>,<f4>,<f5>,<f6>,<f7>", PN_NULL, at_get_channel_up, at_set_channel_up, PN_NULL);

static at_result_t at_get_channel_down(void)
{
    u32 *down = node_config.channel_down;

    at_server_printfln("+DCH=%u,%u,%u,%u,%u,%u,%u,%u", down[0], down[1], down[2], down[3],
                                                       down[4], down[5], down[6], down[7]);
    return AT_RESULT_OK;
}

static at_result_t at_set_channel_down(const char *args)
{
    int argc;
    const char *req_expr = "=%u,%u,%u,%u,%u,%u,%u,%u";
    u32 down[8];

    argc = at_req_parse_args(args, req_expr, &down[0], &down[1], &down[2], &down[3],
                                             &down[4], &down[5], &down[6], &down[7]);
    if(argc != 8)
    {
        return AT_RESULT_PARSE_FAILE;
    }
    
    memcpy(node_config.channel_down, down, sizeof(down));

    return AT_RESULT_OK;
}
AT_CMD_EXPORT("AT+DCH", "=<f0>,<f1>,<f2>,<f3>,<f4>,<f5>,<f6>,<f7>", PN_NULL, at_get_channel_down, at_set_channel_down, PN_NULL);

static at_result_t at_get_rate(void)
{
    at_server_printfln("+RATE=%d", node_config.rate);
    return AT_RESULT_OK;
}

static at_result_t at_set_rate(const char *args)
{
    int rate, argc;
    const char *req_expr = "=%d";

    argc = at_req_parse_args(args, req_expr, &rate);
    if(argc != 1)
    {
        return AT_RESULT_PARSE_FAILE;
    }

    if ( rate <= 0 || rate > 10 )
    {
        return AT_RESULT_PARSE_FAILE;
    }

    at_debug("rate:%u", rate);

    node_config.rate = rate;

    pn3029_config.bw = g_table_rate[rate].bw;
    pn3029_config.sf = g_table_rate[rate].sf;
    pn3029_config.cr = g_table_rate[rate].cr;

    return AT_RESULT_OK;
}
AT_CMD_EXPORT("AT+RATE", "=<rate:1-0.2k,2-0.4k,3-0.5k,4-0.9k,5-1k,6-1.7k,7-3125,8-6.25k,9-10.9k,10-21.8k>", PN_NULL, at_get_rate, at_set_rate, PN_NULL);

static at_result_t at_get_preamlen(void)
{
    rf_config_t *p = &pn3029_config;

    at_server_printfln("+PREAMLEN=%d", p->preamble);

    return AT_RESULT_OK;
}

static at_result_t at_set_preamlen(const char *args)
{
    int preamlen, argc;
    const char *req_expr = "=%d";

    argc = at_req_parse_args(args, req_expr, &preamlen);
    if(argc != 1)
    {
        return AT_RESULT_PARSE_FAILE;
    }

    at_debug("preamlen:%u", preamlen);

    pn3029_config.preamble = preamlen;
    
    return AT_RESULT_OK;
}
AT_CMD_EXPORT("AT+PREAMLEN", "=<preamble len:0~65535>", PN_NULL, at_get_preamlen, at_set_preamlen, PN_NULL);

static at_result_t at_get_preamtime(void)
{
    node_config_t *p = &node_config;

    at_server_printfln("+PREAMBLE=%d", p->preamtime);

    return AT_RESULT_OK;
}

static at_result_t at_set_preamtime(const char *args)
{
    int preamtime, argc;
    const char *req_expr = "=%d";

    argc = at_req_parse_args(args, req_expr, &preamtime);
    if(argc != 1)
    {
        return AT_RESULT_PARSE_FAILE;
    }
    
    if(preamtime < RF_PREAMTIME_MIN 
       || preamtime > RF_PREAMTIME_MAX)
    {
        return AT_RESULT_CHECK_FAILE;
    }

    at_debug("preamtime:%u", preamtime);

    node_config.preamtime = preamtime;

    return AT_RESULT_OK;
}
AT_CMD_EXPORT("AT+PREAMBLE", "=<preamble time:0~10000ms>", PN_NULL, at_get_preamtime, at_set_preamtime, PN_NULL);

static at_result_t at_get_freq(void)
{
    rf_config_t *p = &pn3029_config;
    
    at_server_printfln("+FREQ=%d", p->freq);
    
    return AT_RESULT_OK;
}

static at_result_t at_set_freq(const char *args)
{
    int freq, argc;
    const char *req_expr = "=%d";
    
    argc = at_req_parse_args(args, req_expr, &freq);
    if(argc != 1)
    {
        return AT_RESULT_PARSE_FAILE;
    }
    
    at_debug("freq:%u", freq);
    pn3029_config.freq = freq;

    return AT_RESULT_OK;
}
AT_CMD_EXPORT("AT+FREQ", "=<freq:398000000~900000000>", PN_NULL, at_get_freq, at_set_freq, PN_NULL);

static at_result_t at_get_cr(void)
{
    rf_config_t *p = &pn3029_config;

    at_server_printfln("+CR=%d", p->cr);

    return AT_RESULT_OK;
}

static at_result_t at_set_cr(const char *args)
{
    int cr, argc;
    const char *req_expr = "=%d";

    argc = at_req_parse_args(args, req_expr, &cr);
    if(argc != 1)
    {
        return AT_RESULT_PARSE_FAILE;
    }

    at_debug("cr:%u", cr);
    pn3029_config.cr = cr;

    return AT_RESULT_OK;
}
AT_CMD_EXPORT("AT+CR", "=<coderate:1-CR45,2-CR46,3-CR47,4-CR48>", PN_NULL, at_get_cr, at_set_cr, PN_NULL);

static at_result_t at_get_bw(void)
{
    rf_config_t *p = &pn3029_config;

    at_server_printfln("+BW=%d", p->bw);

    return AT_RESULT_OK;
}

static at_result_t at_set_bw(const char *args)
{
    int bw, argc;
    const char *req_expr = "=%d";

    argc = at_req_parse_args(args, req_expr, &bw);
    if(argc != 1)
    {
        return AT_RESULT_PARSE_FAILE;
    }

    at_debug("bw:%u", bw);
    pn3029_config.bw = bw;

    return AT_RESULT_OK;
}
AT_CMD_EXPORT("AT+BW", "=<bandwith:6-62.5k,7-125k,8-250k,9-500k>", PN_NULL, at_get_bw, at_set_bw, PN_NULL);

static at_result_t at_get_sf(void)
{
    rf_config_t *p = &pn3029_config;

    at_server_printfln("+SF=%d", p->sf);

    return AT_RESULT_OK;
}

static at_result_t at_set_sf(const char *args)
{
    int sf, argc;
    const char *req_expr = "=%d";

    argc = at_req_parse_args(args, req_expr, &sf);
    if(argc != 1)
    {
        return AT_RESULT_PARSE_FAILE;
    }
    
    if(sf < RF_SF_MIN || sf > RF_SF_MAX)
    {
        return AT_RESULT_CHECK_FAILE;
    }

    at_debug("sf:%u", sf);
    pn3029_config.sf = sf;

    return AT_RESULT_OK;
}
AT_CMD_EXPORT("AT+SF", "=<SF:7~12>", PN_NULL, at_get_sf, at_set_sf, PN_NULL);

static at_result_t at_get_power(void)
{
    rf_config_t *p = &pn3029_config;

    at_server_printfln("+RFPOWER=%d", p->pwr);

    return AT_RESULT_OK;
}

static at_result_t at_get_txpower(void)
{
    rf_config_t *p = &pn3029_config;

    at_server_printfln("+RFPOWER=%d", p->pwr);

    return AT_RESULT_OK;
}

static at_result_t at_set_txpower(const char *args)
{
    int pwr, argc;
    const char *req_expr = "=%d";

    argc = at_req_parse_args(args, req_expr, &pwr);
    if(argc != 1)
    {
        return AT_RESULT_PARSE_FAILE;
    }
    
    if(pwr < RF_TXPOWER_MIN || pwr > RF_TXPOWER_MAX)
    {
        return AT_RESULT_CHECK_FAILE;
    }

    at_debug("pwr:%u", pwr);
    pn3029_config.pwr = pwr;

    return AT_RESULT_OK;
}
AT_CMD_EXPORT("AT+RFPOWER", "=<txpower[0~29]>", PN_NULL, at_get_power, at_set_txpower, PN_NULL);

static at_result_t at_get_ldr(void)
{
    rf_config_t *p = &pn3029_config;

    at_server_printfln("+LDR=%d", p->ldr);

    return AT_RESULT_OK;
}

static at_result_t at_set_ldr(const char *args)
{
    int ldr, argc;
    const char *req_expr = "=%d";

    argc = at_req_parse_args(args, req_expr, &ldr);
    if(argc != 1)
    {
        return AT_RESULT_PARSE_FAILE;
    }
    
    if(ldr > 1)
    {
        return AT_RESULT_CHECK_FAILE;
    }

    at_debug("ldr:%u", ldr);
    pn3029_config.ldr = ldr > 0?1:0;

    return AT_RESULT_OK;
}
AT_CMD_EXPORT("AT+LDR", "=<low data rate:0-OFF,1-ON>", PN_NULL, at_get_ldr, at_set_ldr, PN_NULL);

static at_result_t at_get_crc(void)
{
    rf_config_t *p = &pn3029_config;

    at_server_printfln("+CRC=%d", p->crc);

    return AT_RESULT_OK;
}

static at_result_t at_set_crc(const char *args)
{
    int crc, argc;
    const char *req_expr = "=%d";

    argc = at_req_parse_args(args, req_expr, &crc);
    if(argc != 1)
    {
        return AT_RESULT_PARSE_FAILE;
    }
    
    if(crc > 1)
    {
        return AT_RESULT_CHECK_FAILE;
    }

    at_debug("crc:%u", crc);
    pn3029_config.crc = crc > 0?1:0;

    return AT_RESULT_OK;
}
AT_CMD_EXPORT("AT+CRC", "=<crc:0-OFF,1-ON>", PN_NULL, at_get_crc, at_set_crc, PN_NULL);

static at_result_t at_get_dcdc(void)
{
    rf_config_t *p = &pn3029_config;

    at_server_printfln("+DCDC=%d", p->dcdc);

    return AT_RESULT_OK;
}

static at_result_t at_set_dcdc(const char *args)
{
    int dcdc, argc;
    const char *req_expr = "=%d";

    argc = at_req_parse_args(args, req_expr, &dcdc);
    if(argc != 1)
    {
        return AT_RESULT_PARSE_FAILE;
    }
    
    if(dcdc > 1)
    {
        return AT_RESULT_CHECK_FAILE;
    }

    at_debug("dcdc:%u", dcdc);
    pn3029_config.dcdc = dcdc > 0?1:0;

    return AT_RESULT_OK;
}
AT_CMD_EXPORT("AT+DCDC", "=<dcdc:0-OFF,1-ON>", PN_NULL, at_get_dcdc, at_set_dcdc, PN_NULL);

static at_result_t at_get_modem_mode(void)
{
    rf_config_t *p = &pn3029_config;

    at_server_printfln("+MODEMMODE=%d", p->modemMode);

    return AT_RESULT_OK;
}

static at_result_t at_set_modem_mode(const char *args)
{
    int modemMode, argc;
    const char *req_expr = "=%d";

    argc = at_req_parse_args(args, req_expr, &modemMode);
    if(argc != 1)
    {
        return AT_RESULT_PARSE_FAILE;
    }

    if(modemMode != MODEM_MODE_NORMAL
       && modemMode != MODEM_MODE_MULTI_SECTOR)
    {
        return AT_RESULT_PARSE_FAILE;
    }

    at_debug("modemMode:%u", modemMode);
    pn3029_config.modemMode = modemMode;

    return AT_RESULT_OK;
}
AT_CMD_EXPORT("AT+MODEMMODE", "=<moderm:1-normal,2-multy>", PN_NULL, at_get_modem_mode, at_set_modem_mode, PN_NULL);

static at_result_t at_get_rf_addr(void)
{
    node_config_t *p = &node_config;

    at_server_printfln("+RFADDR=%d,%d", p->addr0, p->addr1);

    return AT_RESULT_OK;
}

static at_result_t at_set_rf_addr(const char *args)
{
    int rf_addr0, rf_addr1, argc;
    const char *req_expr = "=%d,%d";

    argc = at_req_parse_args(args, req_expr, &rf_addr0, &rf_addr1);
    if(argc != 2)
    {
        return AT_RESULT_PARSE_FAILE;
    }

    at_debug("rf_addr:%d,%d", rf_addr0, rf_addr1);
    node_config.addr0 = rf_addr0;
    node_config.addr1 = rf_addr1;

    return AT_RESULT_OK;
}
AT_CMD_EXPORT("AT+RFADDR", "=<Addr0:0~255>,<Addr1:0~255>", PN_NULL, at_get_rf_addr, at_set_rf_addr, PN_NULL);

static at_result_t at_get_auxtime(void)
{
    node_config_t *p = &node_config;

    at_server_printfln("+AUXT=%d,%d", p->aux_head_time, p->aux_tail_time);

    return AT_RESULT_OK;
}

static at_result_t at_set_auxtime(const char *args)
{
    int aux_head_time, aux_tail_time, argc;
    const char *req_expr = "=%d,%d";

    argc = at_req_parse_args(args, req_expr, &aux_head_time, &aux_tail_time);
    if(argc != 2)
    {
        return AT_RESULT_PARSE_FAILE;
    }
    
    if(aux_head_time < PIN_AUX_TIME_MIN 
       || aux_head_time > PIN_AUX_TIME_MAX)
    {
        return AT_RESULT_CHECK_FAILE;
    }
    
    if(aux_tail_time < PIN_AUX_TIME_MIN 
       || aux_tail_time > PIN_AUX_TIME_MAX)
    {
        return AT_RESULT_CHECK_FAILE;
    }

    at_debug("rf_addr:%d,%d", aux_head_time, aux_tail_time);
    node_config.aux_head_time = aux_head_time;
    node_config.aux_tail_time = aux_tail_time;

    return AT_RESULT_OK;
}
AT_CMD_EXPORT("AT+AUXT", "=<head time:0~255>,<tail time:0~255>", PN_NULL, at_get_auxtime, at_set_auxtime, PN_NULL);

static at_result_t at_get_auxmode(void)
{
    node_config_t *p = &node_config;

    at_server_printfln("+AUXM=%d", p->aux_mode);

    return AT_RESULT_OK;
}

static at_result_t at_set_auxmode(const char *args)
{
    int aux_mode, argc;
    const char *req_expr = "=%d";

    argc = at_req_parse_args(args, req_expr, &aux_mode);
    if(argc != 1)
    {
        return AT_RESULT_PARSE_FAILE;
    }

    if ( aux_mode > AUX_MODE_FULL )
    {
        return AT_RESULT_PARSE_FAILE;
    }

    at_debug("aux_mode:%d", aux_mode);
    node_config.aux_mode = aux_mode;

    return AT_RESULT_OK;
}
AT_CMD_EXPORT("AT+AUXM", "=<aux_mode:emptymode:0,fullmode:1>", PN_NULL, at_get_auxmode, at_set_auxmode, PN_NULL);

static at_result_t at_get_node_id(void)
{
    node_config_t *p = &node_config;

    at_server_printfln("+NID=%u", p->node_id);

    return AT_RESULT_OK;
}

static at_result_t at_set_node_id(const char *args)
{
    int node_id, argc;
    const char *req_expr = "=%u";

    argc = at_req_parse_args(args, req_expr, &node_id);
    if(argc != 1)
    {
        return AT_RESULT_PARSE_FAILE;
    }

    if ( node_id > 0xFFFFFFFF )
    {
        return AT_RESULT_PARSE_FAILE;
    }

    at_debug("node_id:%u", node_id);
    node_config.node_id = node_id;

    return AT_RESULT_OK;
}
AT_CMD_EXPORT("AT+NID", "=<node id:0~65535>", PN_NULL, at_get_node_id, at_set_node_id, PN_NULL);

static at_result_t at_get_app_id(void)
{
    at_server_printfln("+AID=%d", node_config.app_id);

    return AT_RESULT_OK;
}

static at_result_t at_set_app_id(const char *args)
{
    int app_id, argc;
    const char *req_expr = "=%d";

    argc = at_req_parse_args(args, req_expr, &app_id);
    if(argc != 1)
    {
        return AT_RESULT_PARSE_FAILE;
    }
    
    if ( app_id > 0xFFFF )
    {
        return AT_RESULT_PARSE_FAILE;
    }

    at_debug("app_id:%u", app_id);
    node_config.app_id = app_id;

    return AT_RESULT_OK;
}
AT_CMD_EXPORT("AT+AID", "=<app id:0~255>", PN_NULL, at_get_app_id, at_set_app_id, PN_NULL);

static at_result_t at_get_gw_id(void)
{
    at_server_printfln("+GWID=%d", node_config.gw_id);

    return AT_RESULT_OK;
}

static at_result_t at_set_gw_id(const char *args)
{
    int gw_id, argc;
    const char *req_expr = "=%d";

    argc = at_req_parse_args(args, req_expr, &gw_id);
    if(argc != 1)
    {
        return AT_RESULT_PARSE_FAILE;
    }
    
    if ( gw_id > 0xFFFF )
    {
        return AT_RESULT_PARSE_FAILE;
    }

    at_debug("gw_id:%u", gw_id);
    node_config.gw_id = gw_id;

    return AT_RESULT_OK;
}
AT_CMD_EXPORT("AT+GWID", "=<gateway id:0~255>", PN_NULL, at_get_gw_id, at_set_gw_id, PN_NULL);

static at_result_t at_get_net_class(void)
{
    at_server_printfln("+NETC=%d", node_config.net_class);

    return AT_RESULT_OK;
}

static at_result_t at_set_net_class(const char *args)
{
    int net_class, argc;
    const char *req_expr = "=%d";

    argc = at_req_parse_args(args, req_expr, &net_class);
    if(argc != 1)
    {
        return AT_RESULT_PARSE_FAILE;
    }
    
    if (( net_class != CHIRP_CLASS_A )&&( net_class != CHIRP_CLASS_C ))
    {
        return AT_RESULT_PARSE_FAILE;
    }

    at_debug("net_class:%u", net_class);
    node_config.net_class = (net_class_t)net_class;

    return AT_RESULT_OK;
}
AT_CMD_EXPORT("AT+NETC", "=<CLASS:0-A,1-B,2-C>", PN_NULL, at_get_net_class, at_set_net_class, PN_NULL);

static at_result_t at_get_node_workmode(void)
{
    at_server_printfln("+WMODE=%d", node_config.work_mode);

    return AT_RESULT_OK;
}

static at_result_t at_set_node_workmode(const char *args)
{
    int work_mode, argc;
    const char *req_expr = "=%d";

    argc = at_req_parse_args(args, req_expr, &work_mode);
    if(argc != 1)
    {
        return AT_RESULT_PARSE_FAILE;
    }

    if ( work_mode >= WORKMODE_MAX )
    {
        return AT_RESULT_PARSE_FAILE;
    }

    node_config.work_mode = (workmode_t)work_mode;

    at_server_printfln("OK");

    return AT_RESULT_NULL;
}
AT_CMD_EXPORT("AT+WMODE", "=<workmode:0-PT mode,1-NET mode>", PN_NULL, at_get_node_workmode, at_set_node_workmode, PN_NULL);

static at_result_t at_get_pt_class(void)
{
    at_server_printfln("+PTC=%d", node_config.pt_class);

    return AT_RESULT_OK;
}

static at_result_t at_set_pt_class(const char *args)
{
    int pt_class, argc;
    const char *req_expr = "=%d";

    argc = at_req_parse_args(args, req_expr, &pt_class);
    if(argc != 1)
    {
        return AT_RESULT_PARSE_FAILE;
    }

    if ( pt_class >= PT_CLASS_MAX )
    {
        return AT_RESULT_PARSE_FAILE;
    }

    node_config.pt_class = (pt_class_t)pt_class;

    at_server_printfln("OK");

    return AT_RESULT_NULL;
}
AT_CMD_EXPORT("AT+PTC", "=<class:0-pass through,1-point to point>", PN_NULL, at_get_pt_class, at_set_pt_class, PN_NULL);

static at_result_t at_get_encrypt_enble(void)
{
    char *resp;
    
    if ( node_config.aes_enable )
    {
        resp = STRING_ON;
    }
    else
    {
        resp = STRING_OFF;
    }
    
    at_server_printfln("+ENC=%d", node_config.aes_enable);

    return AT_RESULT_OK;
}

static at_result_t at_set_encrypt_enble(const char *args)
{
    int encrypt_enble, argc;
    const char *req_expr = "=%d";

    argc = at_req_parse_args(args, req_expr, &encrypt_enble);
    if(argc != 1)
    {
        return AT_RESULT_PARSE_FAILE;
    }

    if ( encrypt_enble > 1 )
    {
        return AT_RESULT_PARSE_FAILE;
    }
    
    if ( encrypt_enble )
    {
        node_config.aes_enable = TRUE;
    }
    else
    {
        node_config.aes_enable = FALSE;
    }

    return AT_RESULT_OK;
}
AT_CMD_EXPORT("AT+ENC", "=<enable:0-OFF,1-ON>", PN_NULL, at_get_encrypt_enble, at_set_encrypt_enble, PN_NULL);

static at_result_t at_get_key(void)
{
    char key_str[33];

    key_str[32] = 0;
    
    HexToStr(key_str, node_config.aes_key, 16);
    
    at_server_printfln("+KEY=%s", key_str);

    return AT_RESULT_OK;
}

static at_result_t at_set_key(const char *args)
{
    int argc;
    char key_str[33];
    const char *req_expr = "=%s"; //get up to 64 chars

    key_str[32] = 0;

    /* check the length of setting key 
     * len = len(key)+len(\r\n)=34
     */
    if ( strlen(args+1) != 34 )
    {
        return AT_RESULT_PARSE_FAILE;
    }
    
    argc = at_req_parse_args(args, req_expr, key_str);
    if(argc != 1)
    {
        return AT_RESULT_PARSE_FAILE;
    }

    if(StrToHex(node_config.aes_key, key_str, 16))
    {
        return AT_RESULT_PARSE_FAILE;
    }
    
    at_server_printfln("OK");

    return AT_RESULT_NULL;
}
AT_CMD_EXPORT("AT+KEY", "=<Key:16 HEX String>", PN_NULL, at_get_key, at_set_key, PN_NULL);

static at_result_t at_set_sleep_mode(const char *args)
{
    int rtc_on, sleep_s, argc;
    const char *req_expr = "=%d,%d";

    argc = at_req_parse_args(args, req_expr, &rtc_on, &sleep_s);
    if(argc != 2)
    {
        return AT_RESULT_PARSE_FAILE;
    }

    at_debug("set sleep time:%d seconds.", sleep_s);

    at_debug("rm28 enter sleep mode.");
	
    mcu_enter_lp();
    hw_rtc_cfg(rtc_on, sleep_s); //set rtc

    Lpm_GotoDeepSleep(FALSE); // RTC， UART， RF， IRQ wakeup

    hw_rtc_cfg(FALSE, 31);  // set rtc
    mcu_exit_lp();

    at_debug("rm28 exit sleep mode.");

    return AT_RESULT_OK;
}
AT_CMD_EXPORT("AT+SLEEPMODE", "=<enable:0-OFF,1-ON>,<sleep ms>", PN_NULL, PN_NULL, at_set_sleep_mode, PN_NULL);

static at_result_t pan3029_init(void)
{
    int ret = rf_init();

    if(ret != OK)
    {
        at_debug("rf init err");
        return AT_RESULT_FAILE;
    }
    else
    {
        at_debug("rf init ok");
    }

    user_set_rf_para((rf_config_t *)&pn3029_config);


    return AT_RESULT_OK;
}
AT_CMD_EXPORT("AT+RF_INIT", PN_NULL, PN_NULL, PN_NULL, PN_NULL, pan3029_init);

static at_result_t at_get_rf_para(void)
{
    rf_config_t *p = &pn3029_config;

    at_server_printfln("+RF_PARA=%d,%d,%d,%d,%d,%d,%d,%d,%d,%d",  \
                       p->freq, p->cr, p->bw, p->sf, p->pwr,      \
                       p->crc, p->ldr, p->modemMode, p->preamble, \
                       p->dcdc);

    return AT_RESULT_OK;
}

static at_result_t at_set_rf_para(const char *args)
{
    int freq, cr, bw, sf, txpower, crc, lowdron, modemMode, preamble, dcdc, argc;
    const char *req_expr = "=%d,%d,%d,%d,%d,%d,%d,%d,%d,%d";

    argc = at_req_parse_args(args, req_expr, &freq, &cr, &bw, &sf,
                             &txpower, &crc, &lowdron, &modemMode,
                             &preamble, &dcdc);
    if(argc != 10)
    {
        return AT_RESULT_PARSE_FAILE;
    }

    pn3029_config.freq = freq;
    pn3029_config.cr = cr;
    pn3029_config.bw = bw;
    pn3029_config.sf = sf;
    pn3029_config.pwr = txpower;
    pn3029_config.crc = crc;
    pn3029_config.ldr = lowdron;
    pn3029_config.modemMode = modemMode;
    pn3029_config.preamble = preamble;
    pn3029_config.dcdc = dcdc;
    user_set_rf_para((rf_config_t *)&pn3029_config);

    return AT_RESULT_OK;
}
AT_CMD_EXPORT("AT+RF_PARA", "=<freq:398000000~900000000>,<cr:1~4>,<bw:6~9>,<sf:7~12>,<txp:0~29>,<crc:0/1>,<lowdr:0/1>,<modemMode:1/2>,<preamble>,<dcdc:0/1>", PN_NULL, at_get_rf_para, at_set_rf_para, PN_NULL);

static at_result_t at_rf_tx_ascii(const char *args)
{
    int argc;
    const char *req_expr = "=%s";
    int len;
    int cnt = 0;
    int timeout = 5000;
    
    memset(p_rf_tx_buf, 0, CHIRPLAN_FIFO_LEN);
    
    argc = at_req_parse_args(args, req_expr, p_rf_tx_buf);
    if(argc != 1)
    {
        return AT_RESULT_PARSE_FAILE;
    }

    int ret = rf_init();

    if(ret != OK)
    {
        at_debug("rf init err");
        return AT_RESULT_FAILE;
    }
    else
    {
        at_debug("rf init ok.");
    }

    user_set_rf_para((rf_config_t *)&pn3029_config);

    ret = rf_enter_continous_tx();
    if(ret != OK)
    {
        pc_debug("rf_enter_continous_rx err");
        goto exit;
    }

    len = strlen((char *)p_rf_tx_buf);
    
    /* push datas to rf tx fifo */
    ret = rf_continous_tx_send_data(p_rf_tx_buf, len);
    if ( ret != Ok )
    {
        pc_debug("push fail");
        rf_enter_continous_rx();
        return AT_RESULT_FAILE;
    }

    while ( GPIO_IRQ_LEVEL_GET()==LEVEL_INACTIVE )
    {
        delay1ms(1);
        if ( ++cnt > timeout )
        {
            ret = FAIL;
            goto exit;
        }
    }
    
    at_rf_irq_process();

    if ( at_rf_irq_flag&REG_IRQ_TX_DONE )
    {
        pc_debug("REG_IRQ_TX_DONE");
        ret = OK;
    }
    else
    {
        ret = FAIL;
    }

exit:
    rf_enter_continous_rx();
    
    if ( ret == OK )
    {
        return AT_RESULT_OK;
    }
    else
    {
        return AT_RESULT_FAILE;
    }
}
AT_CMD_EXPORT("AT+TX", "=<asc string>", PN_NULL, PN_NULL, at_rf_tx_ascii, PN_NULL);

static at_result_t at_rf_tx_hex(const char *args)
{
    int argc;
    int len;
    int cnt = 0;
    int timeout = 5000;
    const char *req_expr = "=%s";

    char *p = (char *)p_rf_rx_buf;
    
    memset(p, 0, 512);
    
    argc = at_req_parse_args(args, req_expr, p);
    if(argc != 1)
    {
        return AT_RESULT_PARSE_FAILE;
    }

    len = strlen(p)/2;

    /* if some char is out of range from '0' to 'F', return failure */
    if(StrToHex(p_rf_tx_buf, p, len))
    {
        return AT_RESULT_PARSE_FAILE;
    }

    int ret = rf_init();
    if(ret != OK)
    {
        at_debug("rf init err");
        return AT_RESULT_FAILE;
    }
    else
    {
        at_debug("rf init ok.");
    }

    user_set_rf_para((rf_config_t *)&pn3029_config);

    ret = rf_enter_continous_tx();
    if(ret != OK)
    {
        pc_debug("rf_enter_continous_rx err");
        goto exit;
    }
        
    /* push datas to rf tx fifo */
    ret = rf_continous_tx_send_data(p_rf_tx_buf, len);
    if ( ret != Ok )
    {
        pc_debug("push fail");
        rf_enter_continous_rx();
        return AT_RESULT_FAILE;
    }

    while ( GPIO_IRQ_LEVEL_GET()==LEVEL_INACTIVE )
    {
        delay1ms(1);
        if ( ++cnt > timeout )
        {
            pc_debug("rf tx timeout");
            ret = FAIL;
            goto exit;
        }
    }
    
    at_rf_irq_process();

    if ( at_rf_irq_flag&REG_IRQ_TX_DONE )
    {
        pc_debug("REG_IRQ_TX_DONE");
        ret = OK;
    }
    else
    {
        ret = FAIL;
    }

exit:
    rf_enter_continous_rx();
    
    if ( ret == OK )
    {
        return AT_RESULT_OK;
    }
    else
    {
        return AT_RESULT_FAILE;
    }
}
AT_CMD_EXPORT("AT+TXHEX", "=<hex string>", PN_NULL, PN_NULL, at_rf_tx_hex, PN_NULL);

static at_result_t at_ota_exe(void)
{
    ota_ram_flag = 0x55AA55AA;

    at_server_printfln("OK");
    
    /* reboot node for upgrading*/
    NVIC_SystemReset();
    
	return AT_RESULT_NULL;
}
AT_CMD_EXPORT("AT+OTA", PN_NULL, PN_NULL, PN_NULL, PN_NULL, at_ota_exe);

static at_result_t at_get_dack(void)
{
    node_config_t *p = &node_config;

    at_server_printfln("+DACK=%d", p->net_dack_enbale);

    return AT_RESULT_OK;
}

static at_result_t at_set_dack(const char *args)
{
    int dack, argc;
    const char *req_expr = "=%d";

    argc = at_req_parse_args(args, req_expr, &dack);
    if(argc != 1)
    {
        return AT_RESULT_PARSE_FAILE;
    }
    
    if(dack > 1)
    {
        return AT_RESULT_CHECK_FAILE;
    }

    at_debug("dack:%u", dack);
    node_config.net_dack_enbale = dack > 0?1:0;

    return AT_RESULT_OK;
}
AT_CMD_EXPORT("AT+DACK", "=<net data ack:0-OFF,1-ON>", PN_NULL, at_get_dack, at_set_dack, PN_NULL);

static at_result_t at_get_ackt(void)
{
    node_config_t *p = &node_config;

    at_server_printfln("+ACKT=%d", p->at_ack_type);

    return AT_RESULT_OK;
}

static at_result_t at_set_ackt(const char *args)
{
    int act_type, argc;
    const char *req_expr = "=%d";

    argc = at_req_parse_args(args, req_expr, &act_type);
    if(argc != 1)
    {
        return AT_RESULT_PARSE_FAILE;
    }
    
    if(act_type > 1)
    {
        return AT_RESULT_CHECK_FAILE;
    }

    at_debug("act_type:%u", act_type);
    node_config.at_ack_type = act_type > 0?1:0;
    g_at_ack_type = act_type;
    
    return AT_RESULT_OK;
}
AT_CMD_EXPORT("AT+ACKT", "=<ack:0/1>", PN_NULL, at_get_ackt, at_set_ackt, PN_NULL);

static at_result_t at_set_carrier(const char *args)
{
    int carry_type, argc;
    const char *req_expr = "=%d";

    argc = at_req_parse_args(args, req_expr, &carry_type);

    if(argc != 1)
    {
        return AT_RESULT_PARSE_FAILE;
    }
    
    if(carry_type > 1)
    {
        return AT_RESULT_CHECK_FAILE;
    }
	
	if(carry_type == 1)
	{
		if(PAN3029_set_carrier_wave_on() != OK)
		{
			return AT_RESULT_FAILE;
		}
		
		if(PAN3029_set_tx_power(pn3029_config.pwr) != OK)
		{
			return AT_RESULT_FAILE;
		}
		
		if(PAN3029_set_carrier_wave_freq(pn3029_config.freq) != OK)
		{
			return AT_RESULT_FAILE;
		}
		
	}else{
		if(PAN3029_set_carrier_wave_off() != OK)
		{
			return AT_RESULT_FAILE;
		}
	}

    return AT_RESULT_OK;
}
AT_CMD_EXPORT("AT+CARRIER", "=<carrywave:0/1>", PN_NULL, PN_NULL, at_set_carrier, PN_NULL);

static at_result_t at_get_rxwin(void)
{
    at_server_printfln("+RXWIN=%d", node_config.rf_rx_window);

    return AT_RESULT_OK;
}

static at_result_t at_set_rxwin(const char *args)
{
    int rf_rx_window, argc;
    const char *req_expr = "=%d";

    argc = at_req_parse_args(args, req_expr, &rf_rx_window);
    if(argc != 1)
    {
        return AT_RESULT_PARSE_FAILE;
    }

    node_config.rf_rx_window = rf_rx_window;

    return AT_RESULT_OK;
}
AT_CMD_EXPORT("AT+RXWIN", "=<recv window:1~5000ms>", PN_NULL, at_get_rxwin, at_set_rxwin, PN_NULL);
